package org.hawk.gwt.ppc.reflect;

import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;

import junit.framework.TestCase;

import org.hawk.gwt.ppc.Environment;
import org.hawk.gwt.ppc.ProcessingUnitsManager;
import org.hawk.gwt.ppc.parser.CompilationUnitDescriptor;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.reflect.lang.ClassDelegate;
import org.hawk.gwt.ppc.reflect.mock.Mock2;
import org.hawk.gwt.ppc.reflect.mock.Mock3;
import org.hawk.gwt.ppc.reflect.mock.Mock4;
import org.hawk.gwt.ppc.reflect.mock.Mock8;
import org.hawk.gwt.ppc.reflect.mock.Mock9;
import org.hawk.gwt.ppc.reflect.mock.MyThread;
import org.hawk.gwt.ppc.utils.PreprocessorException;

public class ReflectionCallsTestCase extends TestCase {
	private static final String MOCK02 = Mock2.class.getName();
	private static final String MOCK03 = Mock3.class.getName();
	private static final String MOCK04 = Mock4.class.getName();
	private static final String MOCK08 = Mock8.class.getName();
	private static final String MOCK09 = Mock9.class.getName();
	private static final String MOCK10 = MyThread.class.getName();
	
	@Override
	protected void setUp() throws Exception {
		super.setUp();
		
		ProcessingUnitsManager.getInstance().configure();
	}

	public void testMethod() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK03);
		assertEquals(getMockString(MOCK03)
			.replaceFirst("classToInstantiate.getClass\\(\\)", 
				ClassDelegate.class.getName()+".forClass(" +
				"classToInstantiate.getClass()")
			.replaceFirst("\\).newInstance","\\)\\).newInstance")
			.replaceFirst("Class\\.forName\\(\"\"\\)",
					ClassDelegate.class.getName()+
					"\\.forName(\"\")"),
			stream.toString());
	}
	
	public void testProperty() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK02);
		assertEquals(getMockString(MOCK02)
			.replaceFirst("Mock2.class",  ClassDelegate.class.getName()+".forClass(Mock2.class)")
			.replaceFirst("AnnotationMock.class",  ClassDelegate.class.getName()+".forClass(AnnotationMock.class)"),
			stream.toString());
	}
	
	public void testClass() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK04);
		String mock = getMockString(MOCK04);
		String[] tmp = mock.split("Mock4\\(\\)",2);
		
		assertEquals(tmp[0]+"Mock4()"+(tmp[1]
			.replaceFirst("java.lang.Class",ClassDelegate.class.getName())
			.replaceFirst(" Class"," "+ClassDelegate.class.getName())
			.replaceAll(";", ".toClass();")),
			stream.toString());
	}
	
	public void testCast() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK08);
		String mock = getMockString(MOCK08);
		String[] tmp = mock.split("\\(Class\\<\\? extends Label\\>\\)",2);
		mock = tmp[0]+"(Class<? extends Label>)"+(tmp[1]
			.replaceAll("Class",ClassDelegate.class.getName())
			.replaceFirst(";", ".toClass();"))
			.replaceFirst("cls.newInstance", ClassDelegate.class.getName()+
					".forClass(cls).newInstance")
			.replaceFirst("cls2\\.getMethod\\(",
					ClassDelegate.class.getName()+".forClass(cls2)" +
							".getMethod(");
		assertEquals(mock,
			stream.toString());
	}
	
	public void testVarArgs() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK09);
		assertEquals(getMockString(MOCK09).replaceFirst("Mock3.class", 
				ClassDelegate.class.getName() + ".forClass(Mock3.class)"), stream.toString());
	}
	
	public void testStringConcatenation() throws PreprocessorException, IOException {
		StringWriter stream = new StringWriter();
		scan(stream,MOCK10);
		assertEquals(getMockString(MOCK10).replaceAll("Mock1.class", 
				ClassDelegate.class.getName() + ".forClass(Mock1.class)"), stream.toString());
	}

	private void scan(StringWriter stream, String mockName) throws PreprocessorException {
		ReflectionCallsProcessor processor = new ReflectionCallsProcessor();
		processor.configure(Environment.getInstance().getConfiguration(), ProcessingUnitsManager.getInstance());
		processor.processCompilationUnit(mockName);
		
		CompilationUnitDescriptor cu = ProcessingUnitsManager.getInstance().getRepository().findCompilationUnit(mockName);
		Token tok = cu.getFirstToken();
		while (tok != null) {
			stream.append(tok.getRaw());
			tok = tok.next();
		}
	}

	private String getMockLocation(String mockName) {
		return "./src/"+mockName.replace('.', '/')+".java";
	}

	private String getMockString(String mockName) throws IOException {
		StringWriter stream = new StringWriter();
		
		FileReader reader = null;
		try {
			reader = new FileReader(getMockLocation(mockName));
			
			int len = 0;
			char[] buf = new char[1024];
			while((len = reader.read(buf))>0){
				stream.write(buf,0,len);
			}
		} finally {
			reader.close();
		}
		return stream.toString();
	}
}
